// SPDX-License-Identifier: Apache-2.0
import { clamp01 } from "@thi.ng/math/interval";
import { mix, mixBilinear } from "@thi.ng/math/mix";
import { fract } from "@thi.ng/math/prec";
import { lch } from "./lch/lch.js";

// LUT generated via tools/max-chroma.ts
/** @internal */
const MAX_CHROMA = [
	[
		4, 22, 30, 34, 38, 43, 47, 52, 56, 61, 66, 70, 75, 80, 84, 74, 65, 56,
		47, 39, 32, 25, 18, 11, 5,
	],
	[
		4, 22, 30, 34, 39, 43, 48, 52, 57, 61, 66, 71, 75, 80, 82, 72, 63, 54,
		46, 38, 31, 24, 17, 10, 5,
	],
	[
		4, 22, 31, 35, 39, 44, 49, 53, 58, 63, 67, 72, 77, 82, 83, 73, 63, 54,
		46, 38, 31, 24, 17, 10, 5,
	],
	[
		3, 20, 32, 37, 41, 46, 51, 55, 60, 65, 70, 75, 80, 85, 85, 74, 65, 55,
		47, 39, 31, 24, 17, 10, 5,
	],
	[
		2, 15, 27, 39, 44, 49, 54, 59, 64, 69, 75, 80, 85, 90, 89, 78, 67, 57,
		48, 40, 32, 25, 18, 10, 5,
	],
	[
		2, 11, 22, 32, 43, 53, 59, 64, 70, 76, 81, 87, 93, 99, 96, 84, 72, 61,
		51, 42, 34, 26, 19, 11, 5,
	],
	[
		1, 10, 19, 27, 36, 44, 52, 59, 66, 72, 77, 83, 88, 93, 98, 92, 79, 67,
		56, 45, 36, 28, 20, 12, 6,
	],
	[
		1, 9, 17, 24, 31, 38, 45, 51, 57, 61, 66, 70, 75, 79, 84, 88, 90, 75,
		62, 51, 40, 30, 22, 13, 6,
	],
	[
		1, 8, 15, 22, 28, 35, 40, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86,
		73, 59, 46, 35, 24, 15, 7,
	],
	[
		1, 7, 13, 20, 26, 32, 37, 42, 46, 50, 53, 57, 61, 64, 68, 72, 75, 79,
		83, 72, 55, 41, 29, 18, 8,
	],
	[
		1, 7, 12, 19, 25, 30, 35, 40, 43, 47, 50, 54, 57, 60, 64, 67, 71, 74,
		78, 81, 73, 53, 36, 22, 10,
	],
	[
		1, 7, 12, 19, 24, 29, 34, 38, 42, 45, 48, 51, 55, 58, 61, 65, 68, 71,
		75, 78, 82, 79, 50, 29, 12,
	],
	[
		1, 7, 12, 19, 24, 29, 33, 37, 41, 44, 47, 50, 54, 57, 60, 63, 67, 70,
		73, 77, 80, 83, 86, 48, 19,
	],
	[
		1, 7, 12, 19, 24, 29, 33, 37, 41, 44, 47, 50, 53, 57, 60, 63, 66, 70,
		73, 76, 80, 83, 86, 90, 45,
	],
	[
		1, 7, 12, 19, 25, 30, 34, 38, 41, 44, 48, 51, 54, 58, 61, 64, 67, 71,
		74, 78, 81, 84, 88, 91, 94,
	],
	[
		1, 7, 13, 20, 26, 31, 35, 39, 43, 46, 49, 53, 56, 60, 63, 66, 70, 73,
		77, 80, 84, 87, 91, 94, 60,
	],
	[
		1, 8, 15, 21, 27, 33, 37, 42, 45, 49, 52, 56, 59, 63, 66, 70, 74, 77,
		81, 85, 88, 92, 96, 96, 44,
	],
	[
		1, 9, 16, 23, 29, 35, 40, 45, 49, 52, 56, 60, 64, 68, 72, 76, 80, 84,
		87, 91, 95, 99, 103, 77, 35,
	],
	[
		1, 10, 18, 26, 33, 39, 44, 48, 52, 56, 60, 65, 69, 73, 77, 82, 86, 90,
		94, 99, 103, 107, 110, 65, 30,
	],
	[
		2, 10, 21, 28, 30, 34, 37, 40, 44, 47, 51, 55, 58, 62, 65, 69, 73, 76,
		80, 83, 87, 91, 94, 58, 26,
	],
	[
		1, 10, 18, 24, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 60, 63, 66,
		69, 72, 75, 78, 81, 52, 24,
	],
	[
		1, 8, 15, 21, 23, 26, 28, 31, 33, 36, 39, 42, 44, 47, 50, 53, 55, 58,
		61, 64, 66, 69, 72, 48, 22,
	],
	[
		1, 7, 13, 19, 21, 23, 25, 28, 30, 33, 35, 37, 40, 42, 45, 47, 50, 52,
		55, 57, 60, 62, 65, 46, 21,
	],
	[
		1, 7, 12, 17, 19, 21, 23, 25, 28, 30, 32, 34, 37, 39, 41, 44, 46, 48,
		50, 53, 55, 57, 60, 44, 20,
	],
	[
		1, 6, 11, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 39, 41, 43, 45,
		47, 49, 51, 54, 56, 43, 19,
	],
	[
		1, 6, 11, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43,
		45, 47, 49, 51, 53, 43, 19,
	],
	[
		1, 6, 10, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 35, 37, 39, 41,
		43, 45, 47, 49, 51, 43, 20,
	],
	[
		1, 6, 10, 13, 16, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41,
		43, 45, 46, 48, 49, 33, 16,
	],
	[
		1, 6, 10, 13, 16, 18, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41,
		42, 44, 46, 48, 37, 25, 11,
	],
	[
		1, 7, 11, 13, 16, 18, 20, 22, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41,
		43, 45, 47, 40, 30, 20, 10,
	],
	[
		1, 7, 11, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42,
		44, 46, 43, 34, 26, 17, 8,
	],
	[
		1, 7, 12, 15, 17, 19, 21, 23, 25, 27, 29, 31, 34, 36, 38, 40, 42, 44,
		46, 46, 38, 31, 23, 15, 7,
	],
	[
		1, 8, 13, 16, 18, 20, 22, 25, 27, 29, 31, 33, 36, 38, 40, 42, 45, 47,
		49, 42, 35, 28, 21, 13, 7,
	],
	[
		1, 10, 15, 18, 20, 22, 24, 27, 29, 31, 34, 36, 39, 41, 43, 46, 48, 51,
		46, 40, 33, 26, 19, 12, 6,
	],
	[
		2, 10, 17, 19, 22, 24, 27, 29, 32, 35, 37, 40, 43, 45, 48, 51, 53, 51,
		44, 38, 31, 25, 18, 11, 6,
	],
	[
		2, 13, 19, 22, 25, 28, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 56, 49,
		43, 37, 30, 24, 18, 11, 6,
	],
	[
		3, 17, 22, 26, 29, 32, 36, 39, 42, 46, 49, 53, 56, 60, 63, 62, 55, 49,
		43, 36, 30, 24, 18, 11, 6,
	],
	[
		6, 23, 27, 31, 35, 39, 43, 47, 51, 56, 60, 64, 68, 73, 69, 62, 56, 49,
		43, 36, 30, 24, 18, 11, 6,
	],
	[
		18, 30, 34, 39, 44, 49, 54, 60, 65, 70, 76, 81, 84, 77, 70, 64, 57, 50,
		44, 37, 31, 25, 18, 11, 6,
	],
	[
		12, 41, 45, 52, 59, 66, 73, 80, 87, 94, 102, 95, 88, 81, 74, 66, 60, 53,
		46, 39, 32, 26, 19, 11, 6,
	],
	[
		9, 58, 65, 75, 85, 96, 106, 117, 124, 116, 109, 101, 93, 86, 78, 71, 63,
		56, 49, 42, 34, 27, 20, 12, 6,
	],
	[
		7, 42, 58, 66, 74, 82, 91, 99, 108, 117, 119, 110, 102, 94, 85, 77, 69,
		61, 53, 45, 37, 30, 22, 13, 7,
	],
	[
		6, 34, 47, 53, 60, 67, 74, 81, 88, 95, 102, 109, 114, 105, 95, 86, 77,
		68, 59, 51, 42, 33, 25, 16, 8,
	],
	[
		5, 29, 40, 46, 52, 57, 63, 70, 76, 82, 88, 94, 100, 107, 110, 100, 89,
		79, 69, 58, 48, 38, 29, 19, 9,
	],
	[
		4, 26, 36, 41, 46, 52, 57, 62, 68, 73, 79, 85, 90, 96, 101, 104, 91, 79,
		67, 56, 45, 35, 26, 17, 8,
	],
	[
		4, 24, 34, 38, 43, 48, 53, 58, 63, 68, 73, 78, 83, 88, 94, 91, 79, 69,
		59, 49, 40, 31, 23, 15, 7,
	],
	[
		4, 23, 32, 36, 40, 45, 50, 55, 59, 64, 69, 74, 79, 84, 89, 82, 72, 62,
		53, 44, 36, 28, 20, 12, 6,
	],
	[
		4, 22, 31, 35, 39, 44, 48, 53, 57, 62, 67, 71, 76, 81, 86, 77, 67, 58,
		50, 41, 33, 26, 19, 11, 6,
	],
];

const RES_H = MAX_CHROMA.length;
const RES_L = MAX_CHROMA[0].length;

export const maxChroma = (l: number, h: number) => {
	h = fract(h);
	l = clamp01(l);
	if (l < 1) {
		const h1 = (h * RES_H) | 0;
		const l1 = (l * RES_L) | 0;
		const lutH1 = MAX_CHROMA[h1];
		const lutH2 = MAX_CHROMA[(h1 + 1) % RES_H];
		return (
			mixBilinear(
				lutH1[l1],
				lutH2[l1],
				l1 < RES_L - 1 ? lutH1[l1 + 1] : 0,
				l1 < RES_L - 1 ? lutH2[l1 + 1] : 0,
				h * RES_H - h1,
				l * RES_L - l1
			) * 0.01
		);
	}
	return 0;
};

export const lchMaxChroma = (l: number, h: number, a = 1) =>
	lch(l, maxChroma(l, h), h, a);

export const maxLumaChroma = (h: number) => {
	const h1 = (fract(h) * RES_H) | 0;
	const t = h * RES_H - h1;
	const [l1, c1] = __maxLC(MAX_CHROMA[h1]);
	const [l2, c2] = __maxLC(MAX_CHROMA[(h1 + 1) % RES_H]);
	return { l: mix(l1, l2, t) / RES_L, c: mix(c1, c2, t) * 0.01 };
};

export const lchMaxLumaChroma = (h: number, a = 1) => {
	const max = maxLumaChroma(h);
	return lch(max.l, max.c, h, a);
};

/** @internal */
const __maxLC = (chroma: number[]) => {
	let maxL = 0;
	let maxC = 0;
	for (let i = RES_L; i-- > 0; ) {
		if (chroma[i] >= maxC) {
			maxC = chroma[i];
			maxL = i;
		} else break;
	}
	return [maxL, maxC];
};
